package org.lds.hotkey.console.business.rule.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.lds.hotkey.common.autoconfigure.etcd.EtcdService;
import org.lds.hotkey.common.constant.KeyConstant;
import org.lds.hotkey.common.model.RuleModel;
import org.lds.hotkey.common.model.dto.ResultDTO;
import org.lds.hotkey.common.model.pojo.OrderBy;
import org.lds.hotkey.common.model.pojo.PageResult;
import org.lds.hotkey.common.util.AssertUtil;
import org.lds.hotkey.common.util.JsonUtil;
import org.lds.hotkey.console.business.app.service.AppService;
import org.lds.hotkey.console.business.rule.converter.*;
import org.lds.hotkey.console.business.rule.entity.Rule;
import org.lds.hotkey.console.business.rule.mapper.RuleMapper;
import org.lds.hotkey.console.business.rule.model.request.*;
import org.lds.hotkey.console.business.rule.model.response.*;
import org.lds.hotkey.console.business.rule.service.RuleBaseService;
import org.lds.hotkey.console.common.enums.ConsoleErrorEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 基础服务实现类
 *
 * @author lidongsheng
 * @since 2024-06-05
 */
@Slf4j
@Service
public class RuleBaseServiceImpl implements RuleBaseService {

    @Autowired
    private AppService appService;

    @Autowired
    private RuleMapper ruleMapper;

    @Autowired
    private EtcdService etcdService;

    @Autowired
    private RuleBaseCreateRequestToEntityConverter ruleBaseCreateRequestToEntityConverter;

    @Autowired
    private RuleBaseUpdateRequestToEntityConverter ruleBaseUpdateRequestToEntityConverter;

    @Autowired
    private RuleBaseListRequestToEntityConverter ruleBaseListRequestToEntityConverter;

    @Autowired
    private RuleToBaseResponseConverter ruleToBaseResponseConverter;

    @Autowired
    private RuleBaseListPageRequestToEntityConverter ruleBaseListPageRequestToEntityConverter;

    @Autowired
    private RuleBaseGetByKeyRequestToEntityConverter ruleBaseGetByKeyRequestToEntityConverter;

    @Autowired
    private RuleBaseDeleteRequestToEntityConverter ruleBaseDeleteRequestToEntityConverter;

    @Override
    @Transactional
    public ResultDTO<RuleBaseResponse> createRule(RuleBaseCreateRequest request) {
        appService.existApp(request.getAppId());
        try {
            Rule rule = ruleBaseCreateRequestToEntityConverter.convert(request);
            ruleMapper.insert(rule);

            // 新增成功则同步到ETCD
            boolean success = etcdService.put(KeyConstant.RULE_DIRECTORY + rule.getAppId() + "/" + rule.getId(),
                    JsonUtil.toJson(RuleModel.builder()
                                    .appId(rule.getAppId())
                                    .id(rule.getId())
                                    .key(rule.getKey())
                                    .duration(rule.getDuration())
                                    .interval(rule.getInterval())
                                    .prefix(rule.getPrefix())
                                    .threshold(rule.getThreshold())
                                    .priority(rule.getPriority())
                                    .build()));
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);

            RuleBaseResponse response = ruleToBaseResponseConverter.convert(rule);
            return ResultDTO.success(response);
        } catch (DuplicateKeyException e) {
            return ResultDTO.fail("DATA_REPEAT", "数据重复");
        }
    }

    @Transactional
    @Override
    public ResultDTO<RuleBaseBatchCreateResponse> batchCreateRule(RuleBaseBatchCreateRequest request) {
        RuleBaseBatchCreateResponse response = RuleBaseBatchCreateResponse.builder()
                .responses(request.getRequests().stream().map(this::createRule).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<RuleBaseDeleteResponse> deleteRule(RuleBaseDeleteRequest request) {
        RuleBaseDeleteResponse response = RuleBaseDeleteResponse.builder().request(request).build();

        Rule rule = ruleBaseDeleteRequestToEntityConverter.convert(request);
        List<Rule> rules = ruleMapper.selectList(new QueryWrapper<>(rule));
        if (CollectionUtils.isEmpty(rules)) return ResultDTO.success(response);

        ruleMapper.delete(new QueryWrapper<>(rule));

        for (Rule currRule : rules) {
            boolean success = etcdService.delete(KeyConstant.RULE_DIRECTORY + currRule.getAppId() + "/" + currRule.getId());
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);
        }

        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<RuleBaseBatchDeleteResponse> batchDeleteRule(RuleBaseBatchDeleteRequest request) {
        RuleBaseBatchDeleteResponse response = RuleBaseBatchDeleteResponse.builder()
                .responses(request.getRequests().stream().map(this::deleteRule).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<RuleBaseResponse> updateRule(RuleBaseUpdateRequest request) {
        Rule rule = ruleBaseUpdateRequestToEntityConverter.convert(request);
        RuleBaseResponse response = ruleToBaseResponseConverter.convert(rule);
        Rule currRule = ruleMapper.selectById(request.getId());
        if (currRule == null) return ResultDTO.success(response);

        try {
            ruleMapper.updateById(rule);

            boolean success = updateETCD(request, currRule);
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);

            return ResultDTO.success(response);
        } catch (DuplicateKeyException e) {
            return ResultDTO.fail("DATA_REPEAT", "数据重复");
        }
    }

    private boolean updateETCD(RuleBaseUpdateRequest request, Rule currRule) {
        if (!currRule.getEnable() && !request.getEnable()) return true;
        if (currRule.getEnable() && BooleanUtils.isFalse(request.getEnable())) {
            return etcdService.delete(KeyConstant.RULE_DIRECTORY + currRule.getAppId() + "/" + currRule.getId());
        }

        return etcdService.put(KeyConstant.RULE_DIRECTORY + currRule.getAppId() + "/" + currRule.getId(),
                JsonUtil.toJson(RuleModel.builder()
                        .appId(currRule.getAppId())
                        .id(request.getId())
                        .key(request.getKey())
                        .duration(request.getDuration())
                        .interval(request.getInterval())
                        .prefix(request.getPrefix())
                        .threshold(request.getThreshold())
                        .priority(request.getPriority())
                        .build()));
    }

    @Transactional
    @Override
    public ResultDTO<RuleBaseBatchUpdateResponse> batchUpdateRule(RuleBaseBatchUpdateRequest request) {
        RuleBaseBatchUpdateResponse response = RuleBaseBatchUpdateResponse.builder()
                .requests(request.getRequests().stream().map(this::updateRule).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<RuleBaseResponse> getRuleById(RuleBaseGetByIdRequest request) {
        Rule rule = ruleMapper.selectById(request.getId());
        if (rule == null) {
            return ResultDTO.success();
        }

        RuleBaseResponse response = ruleToBaseResponseConverter.convert(rule);
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<RuleBaseListResponse> getRuleByIds(RuleBaseGetByIdsRequest request) {
        List<Rule> list = request.getIds().stream().map(id -> ruleMapper.selectById(id)).collect(Collectors.toList());
        return ResultDTO.success(RuleBaseListResponse.builder()
                .rules(list.stream().map(rule -> ruleToBaseResponseConverter.convert(rule)).collect(Collectors.toList()))
                .build());
    }

    @Override
    public Rule getRuleByKey(RuleBaseGetByKeyRequest request) {
        if (!request.isHasBusinessKey()) return null;
        return ruleMapper.selectOne(new QueryWrapper<>(ruleBaseGetByKeyRequestToEntityConverter.convert(request)));
    }

    @Override
    public ResultDTO<RuleBaseListResponse> listRule(RuleBaseListRequest request) {
        QueryWrapper<Rule> wrapper = new QueryWrapper<>(ruleBaseListRequestToEntityConverter.convert(request));
        if (StringUtils.isNotEmpty(request.getOrderByKey())) {
            wrapper.orderByDesc(request.getOrderByKey());
        }

        List<Rule> rules = ruleMapper.selectList(wrapper);

        RuleBaseListResponse response = RuleBaseListResponse.builder()
                .rules(rules.stream().map(ruleToBaseResponseConverter::convert).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<RuleBaseListResponse> listAllRule(OrderBy orderBy) {
        RuleBaseListRequest ruleRequest = RuleBaseListRequest.builder().build();
        ruleRequest.setOrderByKey(orderBy.getOrderByKey());
        return listRule(ruleRequest);
    }

    @Override
    public ResultDTO<PageResult<RuleBaseResponse>> listRulePage(RuleBaseListPageRequest request) {
        Page<Rule> page = new Page<>(request.getPageNum(), request.getPageSize(), true);

        QueryWrapper<Rule> wrapper = new QueryWrapper<>(ruleBaseListPageRequestToEntityConverter.convert(request));
        if (StringUtils.isNotEmpty(request.getOrderByKey())) {
            if (request.getOrderByDesc() != null && request.getOrderByDesc().booleanValue() == false) {
                wrapper.orderByAsc(request.getOrderByKey());
            } else {
                wrapper.orderByDesc(request.getOrderByKey());
            }
        }

        IPage<Rule> pageResult = ruleMapper.selectPage(page, wrapper);

        PageResult<RuleBaseResponse> response = new PageResult(
                pageResult.getTotal(), pageResult.getPages(), pageResult.getCurrent(), pageResult.getSize(),
                pageResult.getRecords().stream().map(ruleToBaseResponseConverter::convert).collect(Collectors.toList()));
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<RuleBaseBatchCreateResponse> batchCreateRuleAsync(RuleBaseBatchCreateRequest request) {
        RuleBaseBatchCreateResponse response = RuleBaseBatchCreateResponse.builder()
                .responses(request.getRequests().parallelStream().map(this::createRule).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<RuleBaseBatchDeleteResponse> batchDeleteRuleAsync(RuleBaseBatchDeleteRequest request) {
        RuleBaseBatchDeleteResponse response = RuleBaseBatchDeleteResponse.builder()
                .responses(request.getRequests().parallelStream().map(this::deleteRule).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<RuleBaseBatchUpdateResponse> batchUpdateRuleAsync(RuleBaseBatchUpdateRequest request) {
        RuleBaseBatchUpdateResponse response = RuleBaseBatchUpdateResponse.builder()
                .requests(request.getRequests().parallelStream().map(this::updateRule).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

}
